/*
* Copyright (C) 2020 ~ 2024 chaigec All Rights Reserved.
*
* Author:     chaigec <chaigec@163.com>
* Maintainer: chaigec <chaigec@163.com>

 * Use of this source code is governed by MIT license that can be found in the
 * LICENSE file in the root of the source tree. All contributing project authors
 * may be found in the AUTHORS file in the root of the source tree.
*/

#include "MkSha1.h"
#include "MkUtil.h"
#include "MkLog.h"

#ifdef _WIN32
#define LITTLE_ENDIAN  //windows is litle endian
#endif

#define rol(value, bits) (((value) << (bits)) | ((value) >> (32 - (bits))))

/* blk0() and blk() perform the initial expand. */
/* I got the idea of expanding during the round function from SSLeay */
#if defined(LITTLE_ENDIAN)
#define blk0(i)                                                                \
    (Block->l[i] = (rol(Block->l[i], 24) & 0xFF00FF00) |                       \
                   (rol(Block->l[i], 8) & 0x00FF00FF))
#elif defined(BIG_ENDIAN)
#define blk0(i) Block->l[i]
#else
#error "Endianness not defined!"
#endif
#define blk(i)                                                                 \
    (Block->l[i & 15] = rol(Block->l[(i + 13) & 15] ^ Block->l[(i + 8) & 15] ^ \
                                Block->l[(i + 2) & 15] ^ Block->l[i & 15],     \
                            1))

/* (R0+R1), R2, R3, R4 are the different operations used in SHA1 */
#define R0(v, w, x, y, z, i)                                                   \
    z += ((w & (x ^ y)) ^ y) + blk0(i) + 0x5A827999 + rol(v, 5);               \
    w = rol(w, 30);
#define R1(v, w, x, y, z, i)                                                   \
    z += ((w & (x ^ y)) ^ y) + blk(i) + 0x5A827999 + rol(v, 5);                \
    w = rol(w, 30);
#define R2(v, w, x, y, z, i)                                                   \
    z += (w ^ x ^ y) + blk(i) + 0x6ED9EBA1 + rol(v, 5);                        \
    w = rol(w, 30);
#define R3(v, w, x, y, z, i)                                                   \
    z += (((w | x) & y) | (w & x)) + blk(i) + 0x8F1BBCDC + rol(v, 5);          \
    w = rol(w, 30);
#define R4(v, w, x, y, z, i)                                                   \
    z += (w ^ x ^ y) + blk(i) + 0xCA62C1D6 + rol(v, 5);                        \
    w = rol(w, 30);

class CMkSha1
{
public:
    CMkSha1();
    ~CMkSha1() {}
    Uint32 Transform(const Uint8 buffer[64]);
    Uint32 Update(const Uint8 *pData, Uint32 nLen);
    Uint32 Final(MkString& strOut);
protected:
private:
    Uint32      m_State[5];
    Uint32      m_Count[5];
    Uint8       m_Buffer[64];
};

CMkSha1::CMkSha1()
{
    m_State[0] = 0x67452301;
    m_State[1] = 0xEFCDAB89;
    m_State[2] = 0x98BADCFE;
    m_State[3] = 0x10325476;
    m_State[4] = 0xC3D2E1F0;
    m_Count[0] = m_Count[1] = 0;
}

Uint32 CMkSha1::Transform(const Uint8 buffer[64])
{
    Uint32 a, b, c, d, e;

    typedef union
    {
        Uint8 c[64];
        Uint32 l[16];
    } CHAR64LONG16;

    CHAR64LONG16 Block[1];
    memcpy(Block, buffer, 64);
    a = m_State[0];
    b = m_State[1];
    c = m_State[2];
    d = m_State[3];
    e = m_State[4];
    R0(a, b, c, d, e, 0);
    R0(e, a, b, c, d, 1);
    R0(d, e, a, b, c, 2);
    R0(c, d, e, a, b, 3);
    R0(b, c, d, e, a, 4);
    R0(a, b, c, d, e, 5);
    R0(e, a, b, c, d, 6);
    R0(d, e, a, b, c, 7);
    R0(c, d, e, a, b, 8);
    R0(b, c, d, e, a, 9);
    R0(a, b, c, d, e, 10);
    R0(e, a, b, c, d, 11);
    R0(d, e, a, b, c, 12);
    R0(c, d, e, a, b, 13);
    R0(b, c, d, e, a, 14);
    R0(a, b, c, d, e, 15);
    R1(e, a, b, c, d, 16);
    R1(d, e, a, b, c, 17);
    R1(c, d, e, a, b, 18);
    R1(b, c, d, e, a, 19);
    R2(a, b, c, d, e, 20);
    R2(e, a, b, c, d, 21);
    R2(d, e, a, b, c, 22);
    R2(c, d, e, a, b, 23);
    R2(b, c, d, e, a, 24);
    R2(a, b, c, d, e, 25);
    R2(e, a, b, c, d, 26);
    R2(d, e, a, b, c, 27);
    R2(c, d, e, a, b, 28);
    R2(b, c, d, e, a, 29);
    R2(a, b, c, d, e, 30);
    R2(e, a, b, c, d, 31);
    R2(d, e, a, b, c, 32);
    R2(c, d, e, a, b, 33);
    R2(b, c, d, e, a, 34);
    R2(a, b, c, d, e, 35);
    R2(e, a, b, c, d, 36);
    R2(d, e, a, b, c, 37);
    R2(c, d, e, a, b, 38);
    R2(b, c, d, e, a, 39);
    R3(a, b, c, d, e, 40);
    R3(e, a, b, c, d, 41);
    R3(d, e, a, b, c, 42);
    R3(c, d, e, a, b, 43);
    R3(b, c, d, e, a, 44);
    R3(a, b, c, d, e, 45);
    R3(e, a, b, c, d, 46);
    R3(d, e, a, b, c, 47);
    R3(c, d, e, a, b, 48);
    R3(b, c, d, e, a, 49);
    R3(a, b, c, d, e, 50);
    R3(e, a, b, c, d, 51);
    R3(d, e, a, b, c, 52);
    R3(c, d, e, a, b, 53);
    R3(b, c, d, e, a, 54);
    R3(a, b, c, d, e, 55);
    R3(e, a, b, c, d, 56);
    R3(d, e, a, b, c, 57);
    R3(c, d, e, a, b, 58);
    R3(b, c, d, e, a, 59);
    R4(a, b, c, d, e, 60);
    R4(e, a, b, c, d, 61);
    R4(d, e, a, b, c, 62);
    R4(c, d, e, a, b, 63);
    R4(b, c, d, e, a, 64);
    R4(a, b, c, d, e, 65);
    R4(e, a, b, c, d, 66);
    R4(d, e, a, b, c, 67);
    R4(c, d, e, a, b, 68);
    R4(b, c, d, e, a, 69);
    R4(a, b, c, d, e, 70);
    R4(e, a, b, c, d, 71);
    R4(d, e, a, b, c, 72);
    R4(c, d, e, a, b, 73);
    R4(b, c, d, e, a, 74);
    R4(a, b, c, d, e, 75);
    R4(e, a, b, c, d, 76);
    R4(d, e, a, b, c, 77);
    R4(c, d, e, a, b, 78);
    R4(b, c, d, e, a, 79);
    m_State[0] += a;
    m_State[1] += b;
    m_State[2] += c;
    m_State[3] += d;
    m_State[4] += e;
    a = b = c = d = e = 0;
    memset(Block, '\0', sizeof(Block));
    return NoneError;
}

Uint32 CMkSha1::Update(const Uint8 *pData, Uint32 nLen)
{
    Uint32 i;
    Uint32 j;
    j = m_Count[0];
    if ((m_Count[0] += nLen << 3) < j) {
        m_Count[1]++;
    }

    m_Count[1] += (nLen >> 29);
    j = (j >> 3) & 63;
    if ((j + nLen) > 63) {
        memcpy(&m_Buffer[j], pData, (i = 64 - j));
        Transform(m_Buffer);
        for (; i + 63 < nLen; i += 64) {
            Transform(&pData[i]);
        }
        j = 0;
    } else {
        i = 0;
    }
    memcpy(&m_Buffer[j], &pData[i], nLen - i);
    return NoneError;
}

Uint32 CMkSha1::Final(MkString& strOut)
{
    unsigned i;
    Uint8 FinalCount[8];
    Uint8 c;
    for (i = 0; i < 8; i++) {
        FinalCount[i] = (Uint8)((m_Count[(i >= 4 ? 0 : 1)] >> ((3 - (i & 3)) * 8)) & 255);
    }
    c = 0200;
    Update(&c, 1);
    while ((m_Count[0] & 504) != 448) {
        c = 0000;
        Update(&c, 1);
    }
    Update(FinalCount, 8);
    strOut.clear();
    for (i = 0; i < 20; i++) {
        strOut += (Int8)((m_State[i >> 2] >> ((3 - (i & 3)) * 8)) & 255);
    }
    return NoneError;
}

Uint32 MkSha1Encode(const MkString& strIn, MkString& strOut)
{
    CMkSha1 Sha;
    size_t ii;
    for (ii = 0; ii < strIn.size(); ii += 1) {
        Sha.Update((const Uint8 *)strIn.c_str() + ii, 1);
    }
    return Sha.Final(strOut);
}
